﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using Qas.CommonLogic;
using Qas.CommonLogic.BusinessObjects;
using Qas.User;
using Qas.User.Properties;
using Qas.User.Keepers;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
using System.Windows.Forms;


namespace Qas.Analyzer
{
    public class Program
    {
        // Подгружаемая база данных
        private Base currentBase = null;

        // Множество пред/постусловий и компетенции
        private List<Condition> activePreConditions = new List<Condition>();
        private List<Condition> activePostConditions = new List<Condition>();
        private List<Competence> activeCompetences = new List<Competence>();

        // Импортируем функции из dll
        [DllImport("KERNEL32")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(out long lpFrequency);

        // Первое значение
        private double DFSFirst;
        private double BFSFirst;
        private double BSFirst;
        
        /// <summary>
        /// Конструктор
        /// </summary>
        public Program()
        {
            // Вычисление относительного пути для XML
            string currdir = Environment.CurrentDirectory;
            string[] strarr = currdir.Split(new Char[] { '\\' });

            //string tail = "\\Knowledge\\SimpleBase_simple.xml";
            string tail = "\\Knowledge\\SimpleBase_medium.xml";
            //string tail = "\\Knowledge\\SimpleBase_large.xml";

            int length = strarr.Length - 3;
            string[] strarrCut = new string[length];

            for (int i = 0; i < length; i++)
            {
                strarrCut[i] = strarr[i];
            }

            string head = string.Join("\\", strarrCut);
            string str = string.Concat(head, tail);

            // Загрузка XML
            this.currentBase = LoadBase(str);

            // Фиксация начальных/конечных условий
            foreach (Condition cond in currentBase.ConditionList)
            {
                if (cond.ID == 1)
                {
                    activePreConditions.Add(cond);
                }
                if (cond.ID == 9)
                {
                    activePostConditions.Add(cond);
                }
            }
            
            // Выбор необходимых компетенций
            foreach (Competence comp in currentBase.AvailableCompetences)
            {
                activeCompetences.Add(comp);
            }
        }
        
        /// <summary>
        /// Подгрузка XML
        /// </summary>
        private Base LoadBase(string basePath)
        {
            // Проверяем файл Базы на наличие
            if ((basePath == String.Empty) || (!File.Exists(basePath)))
                throw new NullReferenceException("Файл XML-базы не найден. Путь: " + basePath + "");

            Base result = DataMapper.LoadBaseFromXml(basePath);

            return result;
        }

        /// <summary>
        /// Экспорт в Excel
        /// </summary>
        /// <param name="arr"></param>
        public void ExpToXL(double[,] arr1, double[,] arr2, double[,] arr3, double[,] arr4, double[,] arr5)
        {
            Excel.Application oXL;
           
            Excel._Workbook oWB;
            Excel._Worksheet oSheet;
            Excel.Worksheet newoSheet;
           
            try
            {
                // Запуск Excel и  получение объекта
                oXL = new Excel.Application();
                oXL.Visible = true;

                // Получение новой книги
                oWB = (Excel._Workbook)(oXL.Workbooks.Add(Missing.Value));
                oSheet = (Excel._Worksheet)oWB.ActiveSheet;
                
                // Именуем заголовок клетки и выравниваем по центру
                oSheet.Cells[1, 1] = "DepthFirstSearch";
                oSheet.get_Range("A1", "I1").Font.Bold = true;
                
                // Ограничиваем область для результатов Поиска в глубину
                string upperBorderDFS = "A2";
                string lowerBorderDFS = "A";
                lowerBorderDFS = string.Concat(lowerBorderDFS, (arr1.Length + 1).ToString());

                // Заполняем клетки
                oSheet.get_Range(upperBorderDFS, lowerBorderDFS).Value2 = arr1;

                // Ограничиваем область для результатов Поиска в ширину
                oSheet.Cells[1, 3] = "BreadthFirstSearch";
                string upperBorderBFS = "C2";
                string lowerBorderBFS = "C";
                lowerBorderBFS = string.Concat(lowerBorderBFS, (arr2.Length + 1).ToString());

                // Заполняем клетки
                oSheet.get_Range(upperBorderBFS, lowerBorderBFS).Value2 = arr2;

                // Ограничиваем область для результатов Обратного поиска
                oSheet.Cells[1, 5] = "BackwardSearch";
                string upperBorderBS = "E2";
                string lowerBorderBS = "E";
                lowerBorderBS = string.Concat(lowerBorderBS, (arr3.Length + 1).ToString());

                // Заполняем клетки
                oSheet.get_Range(upperBorderBS, lowerBorderBS).Value2 = arr3;

                // Ограничиваем область для результатов Эвристического поиска
                oSheet.Cells[1, 7] = "HeuristicSearch";
                string upperBorderHS = "G2";
                string lowerBorderHS = "G";
                lowerBorderHS = string.Concat(lowerBorderHS, (arr4.Length + 1).ToString());

                // Заполняем клетки
                oSheet.get_Range(upperBorderHS, lowerBorderHS).Value2 = arr4;

                // Ограничиваем область для результатов Дейкстры
                oSheet.Cells[1, 9] = "DijkstraSearch";
                string upperBorderDij = "I2";
                string lowerBorderDij = "I";
                lowerBorderDij = string.Concat(lowerBorderDij, (arr5.Length + 1).ToString());

                // Заполняем клетки
                oSheet.get_Range(upperBorderDij, lowerBorderDij).Value2 = arr5;

                // Выравнивание
                oSheet.get_Range("A1", "I101").EntireColumn.AutoFit();

                // Вывод характеристик на следующую страницу
                newoSheet = (Excel.Worksheet)oSheet.Next;
                newoSheet.get_Range("A1", "E1").Font.Bold = true;
                newoSheet.get_Range("A1", "A6").Font.Bold = true;
                                
                // Поиск в глубину
                newoSheet.Cells[2, 1] = "DepthFirstSearch";
                
                // Вычисление среднего времени выполнения
                newoSheet.Cells[1, 3] = "Mean execution value";
                string averageTimeCell = "C2";
                newoSheet.get_Range(averageTimeCell, Missing.Value).Formula = "=СРЗНАЧ(Лист1!A2:A101)";

                // Первое значение
                newoSheet.Cells[1, 4] = "First value";// первое значение по-другому
                newoSheet.Cells[2, 4] = DFSFirst;

                // Полное время выполнения
                newoSheet.Cells[1, 5] = "Execution time";
                string executionTimeCell = "E2";
                newoSheet.get_Range(executionTimeCell, Missing.Value).Formula = "=СУММ(Лист1!A2:A101)";

                // Поиск в ширину
                newoSheet.Cells[3, 1] = "BreadthFirstSearch";

                // Вычисление среднего времени выполнения
                newoSheet.get_Range("C3", Missing.Value).Formula = "=СРЗНАЧ(Лист1!C2:C101)";

                // Первое значение
                newoSheet.Cells[3, 4] = BFSFirst;

                // Полное время выполнения
                newoSheet.get_Range("E3", Missing.Value).Formula = "=СУММ(Лист1!C2:C101)";

                // Поиск обратный
                newoSheet.Cells[4, 1] = "BackwardSearch";

                // Вычисление среднего времени выполнения
                newoSheet.get_Range("C4", Missing.Value).Formula = "=СРЗНАЧ(Лист1!E2:E101)";

                // Первое значение
                newoSheet.Cells[4, 4] = BSFirst;

                // Полное время выполнения
                newoSheet.get_Range("E4", Missing.Value).Formula = "=СУММ(Лист1!E2:E101)";

                // Поиск обратный
                newoSheet.Cells[5, 1] = "HeuristicSearch";

                // Вычисление среднего времени выполнения
                newoSheet.get_Range("C5", Missing.Value).Formula = "=СРЗНАЧ(Лист1!G2:G101)";

                //// Первое значение
                //newoSheet.Cells[5, 4] = BSFirst;

                // Полное время выполнения
                newoSheet.get_Range("E5", Missing.Value).Formula = "=СУММ(Лист1!G2:G101)";

                // Поиск обратный
                newoSheet.Cells[6, 1] = "DijkstraSearch";

                // Вычисление среднего времени выполнения
                newoSheet.get_Range("C6", Missing.Value).Formula = "=СРЗНАЧ(Лист1!I2:I101)";

                //// Первое значение
                //newoSheet.Cells[5, 4] = BSFirst;

                // Полное время выполнения
                newoSheet.get_Range("E6", Missing.Value).Formula = "=СУММ(Лист1!I2:I101)";

                // Выравнивание
                newoSheet.get_Range("A1", "G9").EntireColumn.AutoFit();

                oXL.Visible = true;
                oXL.UserControl = true;
            }
            catch (Exception ex)
            {
                string errorMessage;
                errorMessage = "Error: ";
                errorMessage = string.Concat(errorMessage, ex.Message);
                errorMessage = string.Concat(errorMessage, "Line: ");
                errorMessage = string.Concat(errorMessage, ex.Source);

                MessageBox.Show(errorMessage, "Error");
            }
        }

        /// <summary>
        /// Вычисление времени выполнения алгоритма
        /// </summary>
        public double[,] TimeCalculation()
        {
            IKeeper keeper = new BasicK(currentBase);
            
            // Инициализация переменных для подсчета времени
            
            double[,] measuringArr = new double[50, 1];
            double[,] resultingArr = new double[50, 1];
            
            BasicK.measureValue = true;
            List<Plan> plans = new List<Plan>();
            DateTime dtCommon = DateTime.Now;
            
            for (int j = 0; j < 50; j++)
            {
                DateTime dt1 = DateTime.Now;

                for (int i = 0; i < 100; i++)
                {
                    keeper.Search(currentBase.AvailableTasks, activePreConditions, activePostConditions);
                }

                TimeSpan ts = DateTime.Now - dt1;

                measuringArr[j, 0] = ts.TotalMilliseconds / 100;
            }
            Console.WriteLine("Finished" + " " + (DateTime.Now - dtCommon).TotalSeconds);
                
            return measuringArr;            
        }
        
        /// <summary>
        /// Подсчет времени выполнения Эвристик
        /// </summary>
        public double[,] TimeCalculationHeur(bool flag)
        {
            IKeeper keeper;

            if (flag)
            {
                keeper = new HeurKeeper(this.currentBase);
            }
            else
            {
                keeper = new KeeperDijkstra(this.currentBase);
            }

            List<Plan> plans = new List<Plan>();
            // Инициализация переменных для подсчета времени
            
            double[,] measuringArr = new double[50, 1];
            
            BasicK.measureValue = true;

            DateTime dtCommon = DateTime.Now;

            for (int j = 0; j < 50; j++)
            {
                DateTime dt1 = DateTime.Now;
                for (int i = 0; i < 100; i++)
                {
                    keeper.Search(currentBase.AvailableTasks, activePreConditions, activePostConditions);                    
                }

                TimeSpan ts = DateTime.Now - dt1;

                measuringArr[j, 0] = ts.TotalMilliseconds / 100;
            }
            
            Console.WriteLine("Finished" + " " + (DateTime.Now - dtCommon).TotalSeconds);

            return measuringArr;
        }

        static void Main(string[] args)
        {
            Program start = new Program();

            ////// Получение статистики по выполнению Поиска в глубину
            Console.WriteLine("DepthFirtsSearch Data, Processing...\n");
            BasicK.flagALG = "DepthFirstSearch";
            double[,] DFSArr = start.TimeCalculation();
            start.DFSFirst = BasicK.firstResult;
            //double[,] DFSArr = new double[101, 1];

            // Получение статистики по выполнению Поиска в ширину
            Console.WriteLine("BreadthFirstSearch Data, Processing...\n");
            BasicK.flagALG = "BreadthFirstSearch";
            double[,] BFSArr = start.TimeCalculation();
            start.BFSFirst = BasicK.firstResult;
            //double[,] BFSArr = new double[101, 1];

            //// Получение статистики по выполнению Обратного поиска
            Console.WriteLine("BackwardSearch Data, Processing...\n");
            BasicK.flagALG = "BackwardSearch";
            double[,] BSArr = start.TimeCalculation();
            start.BSFirst = BasicK.firstResult;
            //double[,] BSArr = new double[101, 1];
            
            // Получение статистики по Эвристикам
            Console.WriteLine("HeuristicSearch Data, Processing...\n");
            double[,] HSArr = start.TimeCalculationHeur(true);

            // Получение ствтистики по Дейкстре
            Console.WriteLine("DijkstraSearch Data, Processing...\n");
            double[,] DijArr = start.TimeCalculationHeur(false);

            // Вывод информации
            start.ExpToXL(DFSArr, BFSArr, BSArr, HSArr, DijArr);
        }
    }
}
